using Autofac;
using Autofac.Extensions.DependencyInjection;
using Autofac.Extras.DynamicProxy;
using Castle.DynamicProxy;
using EMall.Application;
using EMall.Cache;
using EMall.Domain;
using EMall.Events;
using EMall.Exceptions;
using EMall.Extensions;
using EMall.Middlewares;
using EMall.Query;
using EMall.Uow;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using Serilog;
using System.Reflection;
namespace EMall.OrderService.WebApi
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var assemblies = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "EMall*.dll")
               .Select(o => Assembly.Load(AssemblyName.GetAssemblyName(o))).ToArray();
            var builder = WebApplication.CreateBuilder(args);
            builder.Host.UseSerilog((hostBuilderContext, serviceProvider, loggerConfiguration) =>
            {
                loggerConfiguration.ReadFrom.Configuration(hostBuilderContext.Configuration);
            });
            builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory())
                .ConfigureContainer<ContainerBuilder>(containerBuilder =>
                {
                    #region EventBus
                    containerBuilder.RegisterAssemblyTypes(assemblies)
                    .Where(t => t.IsClass && !t.IsAbstract && t.GetInterfaces().Contains(typeof(IEventBus)) && t.Name.EndsWith("EventBus"))
                    .AsImplementedInterfaces().InstancePerLifetimeScope();
                    #endregion
                    #region UnitOfWorks
                    containerBuilder.RegisterAssemblyTypes(assemblies)
                    .Where(t => t.IsClass && !t.IsAbstract && t.GetInterfaces().Contains(typeof(IUnitOfWork)) && t.Name.EndsWith("UnitOfWork"))
                    .AsImplementedInterfaces().InstancePerLifetimeScope();
                    #endregion
                    #region Interceptors
                    containerBuilder.RegisterAssemblyTypes(assemblies)
                    .Where(t => t.IsClass && !t.IsAbstract && t.GetInterfaces().Contains(typeof(IInterceptor)) && t.Name.EndsWith("Interceptor"));
                    #endregion
                    #region QueryServices
                    containerBuilder.RegisterAssemblyTypes(assemblies)
                    .Where(t => t.IsClass && !t.IsAbstract && t.GetInterfaces().Contains(typeof(IQueryService)) && t.Name.EndsWith("QueryService"))
                    .AsImplementedInterfaces().InstancePerLifetimeScope();
                    #endregion
                    #region ApplicationServices
                    containerBuilder.RegisterAssemblyTypes(assemblies)
                    .Where(t => t.IsClass && t.GetInterfaces().Contains(typeof(IApplicationService)) && t.Name.EndsWith("AppService"))
                    .AsImplementedInterfaces().InstancePerLifetimeScope()
                    .EnableInterfaceInterceptors().InterceptedBy(typeof(UnitOfWorkInterceptor))
                    .PropertiesAutowired();
                    #endregion
                    #region Repositories
                    containerBuilder.RegisterAssemblyTypes(assemblies)
                    .Where(t => t.IsClass && !t.IsAbstract && t.GetInterfaces().Contains(typeof(IRepository)) && t.Name.EndsWith("Repository"))
                    .AsImplementedInterfaces()
                    .InstancePerLifetimeScope();
                    #endregion
                    #region DomainServices 
                    containerBuilder.RegisterAssemblyTypes(assemblies)
                    .Where(t => t.IsClass && !t.IsAbstract && t.GetInterfaces().Contains(typeof(IDomainService)) && t.Name.EndsWith("Manager"));
                    #endregion
                });
            builder.Services.AddControllers(options =>
            {
                options.UseRoutePrefix(new RouteAttribute("OrderService"));
            }).AddJsonOptions(options =>
            {
                options.JsonSerializerOptions.Converters.Add(new DatetimeJsonConverter());
            });
            builder.Services.AddCors(options =>
            {
                var origins = builder.Configuration.GetSection("Cors").Value!.Split(",");
                options.AddDefaultPolicy(config =>
                {
                    config
                    .WithOrigins(origins)
                    .AllowAnyHeader()
                    .AllowAnyMethod()
                    .AllowCredentials();
                });
            });
            builder.Services.AddDbContext<OrderServiceDbContext>(options =>
            {
                options.UseSqlite(builder.Configuration.GetConnectionString("Sqlite"),
                    builder => builder.MigrationsAssembly(Assembly.GetExecutingAssembly().GetName().Name));
                //options.UseMySql(
                //    builder.Configuration.GetConnectionString("Default"),
                //    MySqlServerVersion.LatestSupportedServerVersion,
                //    builder => builder.MigrationsAssembly(Assembly.GetExecutingAssembly().GetName().Name));
            });
            var authServerConfig = builder.Configuration.GetSection("Auth:AuthServer").Get<AuthServerConfig>()!;
            builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                .AddJwtBearer(options =>
                {
                    options.Authority = authServerConfig.Authority;
                    options.RequireHttpsMetadata = authServerConfig.RequireHttpsMetadata;
                    options.TokenValidationParameters = new TokenValidationParameters
                    {
                        ValidateAudience = false
                    };
                });
            builder.Services.AddSwaggerGen(options =>
            {
                options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"), true);
                options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
                {
                    Type = SecuritySchemeType.OAuth2,
                    Flows = new OpenApiOAuthFlows
                    {
                        Password = new OpenApiOAuthFlow
                        {
                            AuthorizationUrl = new Uri(authServerConfig.Authority),
                            TokenUrl = new Uri($"{authServerConfig.Authority}/connect/token"),
                            Scopes = new Dictionary<string, string>
                            {
                                { "openid",""},
                                { "profile",""},
                            },
                        }
                    },
                });
                options.AddSecurityRequirement(new OpenApiSecurityRequirement
                {
                    {
                        new OpenApiSecurityScheme
                        {
                            Reference = new OpenApiReference
                            {
                                Type = ReferenceType.SecurityScheme,
                                Id = "oauth2"
                            }
                        },
                        new string[] { }
                    }
                });
            });
            builder.Services.AddExceptionHandler<ExceptionHandler>();
            builder.Services.AddAutoMapper(assemblies);
            builder.Services.AddMediatR(options =>
            {
                options.RegisterServicesFromAssemblies(assemblies);
            });
            builder.Services.AddCacheManager();
            var app = builder.Build();
            app.UseCors();
            app.UseSwagger();
            app.UseSwaggerUI();
            app.UseAuthentication();
            app.UseAuthorization();
            app.UseRequestLogging();
            app.UseExceptionHandler(app => { });
            app.MapControllers();
            app.Run();
        }
    }
}
